mTLS self-signed pubkey-only certs

2022-04-06 ยท 2 min read

mysten-infra/rccheck

Assuming you already have PKI set up to distribute pubkeys everywhere, this crate allows you to

  1. easily generate valid self-signed certs from a keypair.
  2. server and client verifiers that check whether the peer pubkey is as we expect.

Just like our old NoiseIX handshake code (which was simpler IMO : ))

// rccheck/src/ed25519_certgen.rs

fn gen_certificate(
    subject_names: impl Into<Vec<String>>,
    key_pair: (&[u8], &'static SignatureAlgorithm),
) -> Result<rustls::Certificate, anyhow::Error> {
    let kp = KeyPair::from_der_and_sign_algo(key_pair.0, key_pair.1)?;

    let mut cert_params = CertificateParams::new(subject_names);
    cert_params.key_pair = Some(kp);
    cert_params.distinguished_name = rcgen::DistinguishedName::new();
    cert_params.alg = key_pair.1;

    let cert = rcgen::Certificate::from_params(cert_params).expect(
        "unreachable! from_params should only fail if the key is incompatible with params.algo",
    );
    let cert_bytes = cert.serialize_der()?; // <- also signs
    Ok(rustls::Certificate(cert_bytes))
}

simple Rust library to gen x509 certs #

est31/rcgen

// rcgen/lib.rs
impl CertificateParams {

	// ..

	fn serialize_der_with_signer<K: PublicKeyData>(&self, pub_key: &K, ca :&Certificate) -> Result<Vec<u8>, RcgenError> {
		yasna::try_construct_der(|writer| {
			writer.write_sequence(|writer| {

				let tbs_cert_list_serialized = yasna::try_construct_der(|writer| {
					self.write_cert(writer, pub_key, ca)?;
					Ok::<(), RcgenError>(())
				})?;
				// Write tbsCertList
				writer.next().write_der(&tbs_cert_list_serialized);

				// Write signatureAlgorithm
				ca.params.alg.write_alg_ident(writer.next());

				// Write signature
				ca.key_pair.sign(&tbs_cert_list_serialized, writer.next())?;

				Ok(())
			})
		})
	}

	// ..
}